﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace xpe
{
    /// <summary>
    /// This interface is responsible for creating, activate, and deactivating elements from the object pool
    /// </summary>
    /// <typeparam name="E">The type of element</typeparam>
    public interface IObjectGenerator<E>
    {
        /// <summary>
        /// Creates a new instance of type E
        /// </summary>
        /// <returns>A newly created instance of type E</returns>
        E CreateInstance();

        /// <summary>
        /// Activates the specified element before returning to the user for reuse
        /// </summary>
        /// <param name="element">The element to activate</param>
        void OnActivate(E element);

        /// <summary>
        /// Deactivates the specified element before returing it to the ObjectPool
        /// </summary>
        /// <param name="element">The element to deactivate</param>
        void OnDeactivate(E element);
    }

    /// <summary>
    /// An ObjectPool maintains and release object for reuse
    /// </summary>
    /// <typeparam name="E">The type of elements to hold</typeparam>
    public class ObjectPool<E>
    {
        /// <summary>
        /// The generator
        /// </summary>
        protected IObjectGenerator<E> generator;

        /// <summary>
        /// The queue
        /// </summary>
        protected Queue<E> queue;

        /// <summary>
        /// Create a new ObjectPool with the specified generator
        /// </summary>
        /// <param name="generator">The generator used to prepare and create new instances</param>
        public ObjectPool(IObjectGenerator<E> generator) : this(generator , 0)
        {
        }

        /// <summary>
        /// Create a new ObjectPool with the specified generator and allocates preAllocate elements
        /// </summary>
        /// <param name="generator">The generator used to prepare and create new instances</param>
        /// <param name="preAllocate">The number of object to allocate</param>
        public ObjectPool(IObjectGenerator<E> generator, int preAllocate)
        {
            if (generator == null)
                throw new ArgumentNullException("generator == null");

            this.generator = generator;

            for (int i = 0; i < preAllocate; i++)
                queue.Enqueue(generator.CreateInstance());
        }

        /// <summary>
        /// Returns an element from the object pool.  If the object pool is empty, then a new element will be allocated
        /// using the generator
        /// </summary>
        /// <returns>An element from the object pool</returns>
        public E Get()
        {
            E element;

            if (queue.Count == 0)
                element = generator.CreateInstance();
            else
                element = queue.Dequeue();

            if(element != null)
                generator.OnActivate(element);

            return element;
        }

        /// <summary>
        /// Releases and returns the specified element back into the object pool
        /// </summary>
        /// <param name="element">The element to return to the object pool</param>
        public void Release(E element)
        {
            if (element == null)
                return;

            generator.OnDeactivate(element);

            queue.Enqueue(element);
        }

        /// <summary>
        /// The number of elements in the object pool
        /// </summary>
        public int Count
        {
            get { return queue.Count; }
        }
    }
}
